#pragma once


#include <metalc/block/cobj/_cobj.h>


/* 这个是WF的 */

typedef union cobj_ord {
	byte_t byte;
	struct{
		byte_t used: 1;
		byte_t ord : 7;
	};
} cobj_ord_t;

/* TODO:可以进一步优化pmap过程 */

SAI uvoid *_cobjext_set_cobj(void *ptr, size_t size, bool_t used, cobj_ord_t *map, const size_t min_size){
	ASSERT(is_powerof2(min_size) && is_powerof2(size));
	byte_t ord = log2_align(size) - log2_align(min_size);
	/* word_t nr = size / min_size; */
	word_t cur_idx = (paddr_t)ptr/min_size;
	/* 标记ord以及使用情况 */
	// atm_st(&map[cur_idx].byte, (ord << 1) | (byte_t)(used & 0x1));
	// if (!((map[cur_idx].byte & 0x1) == used)) fatal("???\n");
	/* 0表示未使用 ord + 1编码进去 */
	map[cur_idx].byte = (ord << 1) | (byte_t)(used & 0x1);

	ASSERT((map[cur_idx].byte & 0x1) == used);
	/* printf("[set %lx to %x]\n", cur_idx, used); */
	return (uvoid *)ptr;
}
SAI size_t _cobjext_ptr_getsize(void *ptr, cobj_ord_t *map, const size_t min_size){
	word_t cur_idx = (paddr_t)ptr/min_size;
	byte_t mord = log2_align(min_size);
	byte_t bt = atm_ld(&map[cur_idx].byte);
	byte_t ord = bt >> 1;
	
	word_t size = BIT(ord + mord);
	
	if (!(bt & 0x1)) {
		/* printf("[get %lx, %x]\n", cur_idx, bt);
		printf("[try get size %p, %lx] now bt %x\n", ptr, size, atm_ld(&map[cur_idx].byte)); */
		// printf("now bt %x\n",  atm_ld(&map[cur_idx].byte));
		/* assert(bt & 0x1); */
		return 0;
	}
	return size;
}
SAI size_t _cobjext_mocupysz(){
	return 0;
}
SAI uvoid* _cobjext_ptr2uptr(void *ptr){
	return (uvoid *)ptr;
}
SAI void* _cobjext_uptr2ptr(uvoid *uptr){
	void *ptr = (void *)uptr;
	
	return ptr;
}
SAI void* _cobjext_get_prev(void *fst, size_t fstsize){
	/* 返回的是其buddy页 */
	ASSERT(is_powerof2(fstsize));
	return (void *)((paddr_t)fst ^ fstsize); 
}
SAI size_t _cobjext_merge_next(void *fst, size_t fstsize, cobj_ord_t *map, const size_t min_size){
	/* 尝试合并，只有当前和下一个非使用状态才能合并 */
	/* 入参之一一定是未使用的内存 */
	word_t cur_idx = (paddr_t)fst/min_size;
	word_t next_idx = ((paddr_t)fst + fstsize) / min_size;
	byte_t cur_byte = map[cur_idx].byte;
	byte_t next_byte = map[next_idx].byte;

	/* 不能合并 */
	// if ((cur_byte & 0x1) | (next_byte & 0x1)) return fstsize;
	/* 可简化为二者不等 */
	if (cur_byte != next_byte) return fstsize;
	/* 此处cur_byte的最后一位一定为0 */
	// byte_t mord = log2_align(min_size);
	// word_t size0 = BIT((cur_byte >> 1) + mord);
	// word_t size1 = BIT((next_byte >> 1) + mord);
	// if (size0 != size1 || size0 != fstsize){ printf("s0%lx s1%lx, fstsz%lx\n", size0, size1, fstsize); }
	// ASSERT(size0 == size1);
	// ASSERT(size0 == fstsize);
	/* 升阶 (阶数 + 1) */
	map[cur_idx].byte = cur_byte + 2;
	return fstsize << 1;
}
/* 切割 */
SAI void *_cobjext_split(void *ptr, size_t size, size_t newsize, cobj_ord_t *map, const size_t min_size){
	ASSERT(is_powerof2(min_size) && is_powerof2(size) && is_powerof2(newsize));
	if (size != newsize * 2) return NULL;
	/* 假设成立后 */
	byte_t nord = log2_align(newsize) - log2_align(min_size);
	void *next = (void *)((paddr_t)ptr + newsize);
	word_t cur_idx = (paddr_t)ptr/min_size;
	word_t next_idx = ((paddr_t)ptr + newsize)/min_size;

	map[cur_idx].byte = (nord << 1);
	map[next_idx].byte = (nord << 1);

	return next;
}

#define COBJ_EXT(NAME, pmap, MIN_SIZE)\
SAI uvoid *cobjext_set_cobj(void *ptr, size_t size, bool_t used){ return _cobjext_set_cobj(ptr, size, used, pmap, MIN_SIZE); }\
SAI size_t cobjext_ptr_getsize(void *ptr){ return _cobjext_ptr_getsize(ptr, pmap, MIN_SIZE);}\
SAI size_t cobjext_mocupysz(){ return _cobjext_mocupysz(); }\
SAI uvoid* cobjext_ptr2uptr(void *ptr){ return _cobjext_ptr2uptr(ptr); }\
SAI void* cobjext_uptr2ptr(uvoid *uptr){ return _cobjext_uptr2ptr(uptr);}\
SAI void *cobjext_get_prev(void *fst, size_t fstsize){ return _cobjext_get_prev(fst, fstsize); }\
SAI size_t cobjext_merge_next(void *fst, size_t fstsize){ return _cobjext_merge_next(fst, fstsize, pmap, MIN_SIZE);}\
SAI void *cobjext_split(void *ptr, size_t size, size_t newsize, bool_t link_next){ PARAM_USED(link_next);\
return _cobjext_split(ptr, size, newsize, pmap, MIN_SIZE);}\
NEW_OUFUN(cobj, NAME, cobjext_set_cobj, cobjext_ptr_getsize, cobjext_mocupysz, cobjext_ptr2uptr, cobjext_uptr2ptr, cobjext_get_prev, cobjext_merge_next, cobjext_split)
